<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试用例</title>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.js"></script>

    <style>

        html, body {
            margin: 0;
        }

        .rotate-svg {
            transform: rotate(90deg);
        }

        .item-active {
            background: orangered;
            color: white;
        }

        .global-tip {
            left: 40%;
            top: 6px;
            position: absolute;
            width: 200px;
            height: 30px;
            border-radius: 8px;
            display: none;
            color: grey;
            padding: 10px 8px 0px 12px;
            background: white;
            box-shadow: 0 4px 12px rgba(0, 0, 0, .15);
            pointer-events: all;
        }

        .page {
            width: 100%;
            height: 800px;
            display: flex;

            .left {
                width: 160px;
                height: 100%;
                display: flex;
                flex-direction: column;
                align-items: center;
                border-right: #e4dada 1px solid;

                .left-item {
                    color: blue;
                    cursor: pointer;
                    border-radius: 8px;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    width: 140px;
                    height: 60px;
                    background: #dddddd;
                    margin-top: 20px;
                }

            }

            .middle {
                width: 240px;
                height: 100%;
                border-right: #e4dada 1px solid;
                display: flex;
                flex-direction: column;
                align-items: center;

                .middle-item {
                    cursor: pointer;
                    margin-top: 10px;
                    width: 90%;
                    height: max-content;

                    .middle-item-row {
                        display: flex;
                        align-items: center;
                        width: 100%;
                        height: 30px;

                        .middle-item-icon {
                            width: 50px;
                            height: 100%;
                            display: flex;
                            justify-content: center;
                            align-items: center;

                            .icon {
                                display: flex;
                                justify-content: center;
                                align-items: center;
                                width: 20px;
                                height: 20px;
                                background: black;
                                color: white;
                                border-radius: 50px;
                            }
                        }

                        .middle-item-name {
                            display: flex;
                            align-items: center;
                            width: 200px;
                            height: 100%;
                            color: black;
                            font-weight: bold;
                        }

                    }

                    .middle-item-list {
                        width: 100%;
                        height: max-content;

                        .option-item {
                            display: flex;
                            align-items: center;
                            padding-left: 40px;
                            width: 180px;
                            height: 30px;
                            color: #068a4d;
                        }

                    }

                }

            }

            .right {
                width: 840px;
                height: 100%;
                display: flex;

                .right-a {
                    width: 30%;
                    height: 100%;
                    display: flex;
                    flex-direction: column;
                    align-items: center;

                    .right-a-item {
                        cursor: pointer;
                        color: white;
                        display: flex;
                        justify-content: center;
                        align-items: center;
                        width: 90%;
                        height: 40px;
                        background: #1890ff;
                        margin-top: 10px;
                    }

                }

                .right-b {
                    width: 70%;
                    height: 100%;
                    display: flex;
                    justify-content: center;
                    position: relative;

                    .json-editor {
                        overflow-y: scroll;
                        padding-left: 20px;
                        padding-top: 20px;
                        color: #43ff00;
                        width: 600px;
                        height: 520px;
                        border-right: 8px;
                        background: rgb(25, 26, 35);
                        font-size: 14px;
                        line-height: 20px;
                    }

                    .json-copy {
                        cursor: pointer;
                        width: 20px;
                        height: 20px;
                        position: absolute;
                        top: 16px;
                        right: 60px;
                    }

                    .json-copy svg {
                        width: 24px;
                        height: 24px;
                    }


                    .json-format {
                        cursor: pointer;
                        width: 20px;
                        height: 20px;
                        position: absolute;
                        top: 16px;
                        right: 30px;
                    }

                    .json-format svg {
                        width: 20px;
                        height: 20px;
                    }

                }
            }
        }

    </style>

</head>
<body>

<div class="page">
    <div id="left" class="left">
        <div class="left-item">测试场景管理</div>
        <div class="left-item">历史执行记录</div>
    </div>
    <div id="middle" class="middle">
    </div>
    <div class="right">
        <div class="right-a">
            <div class="right-a-item">协议详情</div>
            <div class="right-a-item">是否跳过</div>
            <div class="right-a-item">执行顺序</div>
            <div class="right-a-item">断言</div>
        </div>
        <div class="right-b">
            <textarea id="json-editor" maxlength="10000" class="json-editor" rows="25"></textarea>
            <div id="json-copy" class="json-copy">
                <svg t="1699780172832" class="icon" viewBox="0 0 1024 1024" version="1.1"
                     xmlns="http://www.w3.org/2000/svg" p-id="22005" width="200" height="200">
                    <path d="M795.648 762.0096H419.1744c-80.8448 0-146.432-65.536-146.432-146.432V239.104c0-80.8448 65.536-146.432 146.432-146.432h376.4736c80.8448 0 146.432 65.536 146.432 146.432v376.4736c0 80.896-65.536 146.432-146.432 146.432z"
                          fill="#FFDF99" p-id="22006"></path>
                    <path d="M604.8256 939.776H228.352c-80.8448 0-146.432-65.536-146.432-146.432V416.9216c0-80.8448 65.536-146.432 146.432-146.432h376.4736c80.8448 0 146.432 65.536 146.432 146.432v376.4736c-0.0512 80.8448-65.5872 146.3808-146.432 146.3808z"
                          fill="#FF5D50" p-id="22007"></path>
                    <path d="M552.96 553.9328H467.7632V468.7872c0-28.2624-22.9376-51.2-51.2-51.2s-51.2 22.9376-51.2 51.2v85.1456H280.2176c-28.2624 0-51.2 22.9376-51.2 51.2s22.9376 51.2 51.2 51.2h85.1456v85.1456c0 28.2624 22.9376 51.2 51.2 51.2s51.2-22.9376 51.2-51.2v-85.1456H552.96c28.2624 0 51.2-22.9376 51.2-51.2s-22.9376-51.2-51.2-51.2z"
                          fill="#FFDF99" p-id="22008"></path>
                </svg>
            </div>
            <div id="json-format" class="json-format">
                <svg t="1699780218652" class="icon" viewBox="0 0 1024 1024" version="1.1"
                     xmlns="http://www.w3.org/2000/svg" p-id="29477" width="200" height="200">
                    <path d="M0 0m136.533333 0l750.933334 0q136.533333 0 136.533333 136.533333l0 750.933334q0 136.533333-136.533333 136.533333l-750.933334 0q-136.533333 0-136.533333-136.533333l0-750.933334q0-136.533333 136.533333-136.533333Z"
                          fill="#FFC738" p-id="29478"></path>
                    <path d="M458.533547 324.07552l-80.554667-10.704213-36.0448-66.60096-36.099413 66.60096-80.554667 10.704213 58.272427 51.882667-13.666987 73.23648 72.04864-34.57024 72.04864 34.57024-13.735253-73.23648z"
                          fill="#EAB83A" p-id="29479"></path>
                    <path d="M799.061333 782.336l-66.628266 39.1168c-12.588373 7.386453-22.89664-0.300373-22.89664-17.08032v-78.506667a51.52768 51.52768 0 0 1 23.442773-42.51648l65.536-33.50528c12.888747-6.608213 23.442773 1.733973 23.442773 18.51392v70.014294a55.66464 55.66464 0 0 1-22.89664 43.963733z m-2.921813-144.861867L726.357333 674.474667a56.64768 56.64768 0 0 1-47.22688 0l-72.21248-38.229334a13.312 13.312 0 0 1-0.2048-25.613653l67.447467-37.860693a54.613333 54.613333 0 0 1 47.02208-0.546134l74.943147 40.05888a12.970667 12.970667 0 0 1 0.013653 25.1904z m-116.381013-117.650773l-26.815147-30.037333-39.908693 5.010773 20.261546-34.679467-17.134933-36.427093 39.335253 8.56064 29.354667-27.538773 4.027733 40.045226 35.26656 19.401387-36.864 16.192853z m21.722453-167.44448L669.682347 327.68l-38.393174 12.151467 13.653334-37.84704-23.42912-32.768 40.236373 1.365333 23.906987-32.385707 11.18208 38.66624 38.229333 12.724907-33.286827 22.555307zM353.621333 790.801067a39.840427 39.840427 0 0 1-55.05024 10.390186l-17.312426-11.851093a39.594667 39.594667 0 0 1-10.390187-54.872747l213.947733-312.456533a39.826773 39.826773 0 0 1 55.05024-10.390187l17.353387 11.83744a39.594667 39.594667 0 0 1 10.36288 54.8864z m187.419307-343.927467l-14.158507-9.557333-3.140266-2.184534a11.01824 11.01824 0 0 0-15.428267 2.90816L480.13312 479.232l35.607893 24.234667 28.166827-41.178454a11.168427 11.168427 0 0 0-2.894507-15.428266z m9.038507-123.767467l-19.8656-3.249493-13.953707 14.513493-3.03104-19.879253-18.077013-8.833707 17.98144-9.05216 2.730666-19.879253 14.158507 14.29504 19.810987-3.536213-9.216 17.885866z m-108.270934 105.608534l-100.88448-19.797334-73.5232 71.857494-12.288-102.004054-91.067733-47.7184 93.252267-43.29472 17.257813-101.321386 69.986987 75.28448 101.730986-14.90944-50.025813 89.770666z m-22.28224-164.345174l-80.254293 12.77952-53.589333-53.507413-15.48288 74.1376-74.1376 33.286827 70.683306 33.041066 7.7824 74.069334 59.159894-53.73952 78.92992 12.520106-34.133334-66.259626zM604.678827 649.6256l65.194666 32.058027a50.681173 50.681173 0 0 1 23.606614 42.420906v79.694507c0 16.930133-10.40384 24.94464-23.087787 17.749333l-66.218667-37.382826a54.6816 54.6816 0 0 1-23.087786-43.854507V668.8768c-0.013653-16.971093 10.622293-25.627307 23.59296-19.2512zM791.893333 955.733333v-27.306666h109.226667v-13.653334h-13.653333v-13.653333h-13.653334v-13.653333h-13.653333v-13.653334h-13.653333v-13.653333h-13.653334v-13.653333h-13.653333v-13.653334h-13.653333v-13.653333h-13.653334v-13.653333h13.653334v-13.653334h13.653333v13.653334h13.653333v13.653333h13.653334v13.653333h13.653333v13.653334h13.653333v13.653333h13.653334v13.653333h13.653333v13.653334h13.653333v13.653333h13.653334v-109.226667h27.306666v163.84H791.893333z m70.314667-68.266666zM860.16 887.466667z"
                          fill="#30384F" p-id="29480"></path>
                </svg>
            </div>
        </div>
    </div>

    <div id="global-tip-copy-success" class="global-tip">
        <span style="color: #0ae70a; font-weight: bolder; margin-right: 6px">✔</span>复制成功!
    </div>
    <div id="global-tip-format-success" class="global-tip">
        <span style="color: #0ae70a; font-weight: bolder; margin-right: 6px">✔</span>格式化成功!
    </div>

</div>

<script>

    document.addEventListener("DOMContentLoaded", function () {

        // 左侧菜单点击监听
        var left = document.getElementById("left");
        // 设置左侧菜单默认的选中样式
        left.childNodes[1].style.background = 'orange'
        left.childNodes[1].style.color = 'white'
        // 循环设置单个点击监听
        for (let i = 0; i < left.childNodes.length; i++) {
            left.childNodes[i].onclick = function () {
                left.childNodes[i].style.background = 'orange'
                left.childNodes[i].style.color = 'white'
                for (let j = 0; j < left.childNodes.length; j++) {
                    if (j !== i) { // 排除 #text, 监听的是 HTMLElement 元素类型
                        left.childNodes[j].style && (left.childNodes[j].style.background = '#ddd')
                        left.childNodes[j].style && (left.childNodes[j].style.color = 'blue')
                    }
                }
            }
        }

        // 模拟后端返回的 json 数据, 后端需要按这个格式返回, 真实数据使用接口请求然后替换这里即可
        let arr = [
            {
                id: '1',
                name: '测试阶段1',
                list: [
                    {
                        id: '01',
                        name: '测试用例1',
                        json: '{"userInfo":{"userId":"e68045f6d2e1e02ff8c5f91818260d66","username":"小e","mobile":"13617783616","email":"112@qq.com","avatar":"http://localhost:8080/xxx.png","address":"广东省深圳深圳市xxx","roles":["admin","guest"]},"appId":"8ff8a9a656d4f39666a8fe91d19ba75d","appName":"我的","appVersion":"1.0.0","tenantId":"45f405607e2e537a82b610fbb27ef4f6"}'
                    },
                    {
                        id: '02',
                        name: '测试用例2',
                        json: '{\n' +
                            '    "username": "string",\n' +
                            '    "nickname": "string",\n' +
                            '    "gender": 0,\n' +
                            '    "avatar": "string",\n' +
                            '    "password": "string",\n' +
                            '    "mobile": "string",\n' +
                            '    "email": "string",\n' +
                            '    "status": 0\n' +
                            '}'
                    }
                ]
            },
            {
                id: '2',
                name: '测试阶段2',
                list: [
                    {
                        id: '11',
                        name: '自定义新增用例',
                        json: '{\n' +
                            '    "name": "问信进府品",\n' +
                            '    "parentId": "79",\n' +
                            '    "code": "36",\n' +
                            '    "type": 27,\n' +
                            '    "id": "32"\n' +
                            '}'
                    },
                    {
                        id: '22',
                        name: '查询客户测试用例',
                        json: '{\n' +
                            '    "branchNo": "dolore pariatur esse in commodo",\n' +
                            '    "deviceName": "步最置",\n' +
                            '    "deviceType": 43,\n' +
                            '    "status": 59\n' +
                            '}'
                    },
                    {
                        id: '33',
                        name: '更新物流测试用例',
                        json: '{\n' +
                            '    "name": "因商难列",\n' +
                            '    "parentId": "17",\n' +
                            '    "code": "82",\n' +
                            '    "type": 50,\n' +
                            '    "id": "6"\n' +
                            '}'
                    },
                    {
                        id: '44',
                        name: '新增用户测试用例',
                        json: '{\n' +
                            '    "durationType": 1,\n' +
                            '    "rentAmt": 60,\n' +
                            '    "depositAmt": 19,\n' +
                            '    "carId": "6771244331329978373"\n' +
                            '}'
                    },
                    {
                        id: '55',
                        name: '查询车辆信息测试用例',
                        json: '{\n' +
                            '    "pageNum": 0,\n' +
                            '    "pageSize": 10,\n' +
                            '    "username": "",\n' +
                            '    "mobile": ""\n' +
                            '}'
                    }
                ]
            }
        ]

        // 初始化 HTML 模板
        var template = "<div id=\"middle-item-1\" class=\"middle-item\">\n" +
            "            <div id=\"middle-item-row\" class=\"middle-item-row\">\n" +
            "                <div class=\"middle-item-icon\">\n" +
            "                   <div class=\"icon\">{{ index }}</div> \n" +
            "                </div>\n" +
            "                <div class=\"middle-item-name\">{{ name }}</div>\n" +
            "            </div>\n" +
            "  {{ middleItemContent }} \n"
        "        </div>";

        // 生成循环内部内容
        var innerContent = '';
        $.each(arr, function (index, item) {

            // 外层设置一个 div, 最后是取它里边的内容(通过 innerHTML), 不包括它自身
            var wrapper = document.createElement('div');

            // 创建 middle-item-list div 元素
            var middleItemListDiv = document.createElement('div');
            middleItemListDiv.classList.add('middle-item-list')
            middleItemListDiv.id = `middle-item-list-${item.id}`

            // 循环后端返回的 json 数据创建 option-item div 元素, 即测试用例
            $.each(item.list, function (_index, _item) {
                var optionItemDiv = document.createElement('div');
                optionItemDiv.classList.add('option-item')
                optionItemDiv.innerText = _item.name
                optionItemDiv.id = `option-item-${_item.id}`
                middleItemListDiv.append(optionItemDiv)
            });

            wrapper.append(middleItemListDiv)

            // 将数据源的每个元素插入到模板中的占位符
            var content = template
                .replace('{{ name }}', item.name)
                .replace('{{ index }}', index + 1)
                .replace('{{ middleItemContent }}', wrapper.innerHTML);
            innerContent += content;

        });
        // 渲染结果
        $('#middle').append(innerContent);

        // 获取 option-item 的 id 集合, 用于获取元素
        const collectOptions = (arr) => {
            let tmp = []
            for (const arrElement of arr) {
                arrElement.list.map(x => x.id).forEach(v => tmp.push(v))
            }
            return tmp
        }

        // 根据 id 获取 arr 中 list 中的 item 项, 获取 json 数据
        const selectOptionItem = (arr, id) => {
            var selectOption = {}
            for (const datum of arr) {
                for (const option of datum.list) {
                    if (option.id === id) {
                        selectOption = option
                        break
                    }
                }
            }
            return selectOption
        }

        // 设置默认选中的测试用例

        // 进行 json 格式化
        var defaultJsonData = JSON.stringify(JSON.parse(arr[0].list[0].json), null, '\t')
        // 重新赋新值
        $("#json-editor").val(defaultJsonData)
        // 设置 option-item 选中样式
        document.getElementById(`option-item-${arr[0].list[0].id}`).style.background = '#10320b'
        document.getElementById(`option-item-${arr[0].list[0].id}`).style.color = 'white'

        // 设置 option-item 的监听事件, 以循环的方式进行, 注意循环设置事件闭包的问题
        for (let i = 0; i < collectOptions(arr).length; i++) {
            // 闭包解决循环监听
            (function (i) { // 这里的 i 是函数内部自己的

                var optionId = collectOptions(arr)[i]

                var optionDiv = document.getElementById(`option-item-${optionId}`);
                optionDiv.onclick = function () {
                    console.log(optionDiv)
                    // 展示 json 数据, 先获取 json 数据
                    var selectOption = selectOptionItem(arr, optionId);
                    let jsonData = selectOption.json
                    // 进行 json 格式化
                    var newJsonData = JSON.stringify(JSON.parse(jsonData), null, '\t')
                    // 重新赋新值
                    $("#json-editor").val(newJsonData)
                    // 设置 option-item 选中样式
                    optionDiv.style.background = '#10320b'
                    optionDiv.style.color = 'white'
                    // 设置互斥的样式
                    var tmp = collectOptions(arr).filter(k => k !== optionId)
                    console.log(tmp)
                    for (const tmpId of tmp) {
                        document.getElementById(`option-item-${tmpId}`).style.background = 'white';
                        document.getElementById(`option-item-${tmpId}`).style.color = '#068a4d';
                    }
                }
            })(i); // 这里的i是全局的

        }

        // 复制监听
        var copy = document.getElementById("json-copy");
        copy.onclick = function () {
            const input = document.createElement('input')
            document.body.appendChild(input)
            // 获取 textarea 中的修改后的值
            var currentText = document.getElementById("json-editor").value
            let copyJsonData = JSON.stringify(JSON.parse(currentText), null, '\t')
            input.setAttribute('value', copyJsonData)
            input.select()
            if (document.execCommand('copy')) {
                document.execCommand('copy')
            }
            document.body.removeChild(input)
            var globalTip = document.getElementById("global-tip-copy-success")
            globalTip.style.display = 'block'
            setTimeout(() => {
                globalTip.style.display = 'none'
            }, 2000)
        }

        // 格式化监听
        var format = document.getElementById("json-format");
        format.onclick = function () {

            // 获取 textarea 中的修改后的值
            var currentText = document.getElementById("json-editor").value
            let newJsonData = JSON.stringify(JSON.parse(currentText), null, '\t')
            // 重新赋新值
            $("#json-editor").val(newJsonData)

            var globalTip = document.getElementById("global-tip-format-success")
            globalTip.style.display = 'block'
            setTimeout(() => {
                globalTip.style.display = 'none'
            }, 2000)
        }

        // 设置 textarea 的 tab 按键事件失效问题
        var textareaEl = document.getElementById('json-editor');
        textareaEl.onkeydown = () => {
            if (event.key === "Tab") {    //  单行或者多行缩进
                event.preventDefault();
                let selectionStart = textareaEl.selectionStart;
                let selectionEnd = textareaEl.selectionEnd;
                if (selectionEnd !== selectionStart) {
                    let textValue = textareaEl.value;
                    let strBefore = textValue.slice(0, selectionStart);
                    let lineStart = strBefore.lastIndexOf('\n') + 1;
                    textareaEl.setRangeText('  ', lineStart, lineStart);
                    strBetween = textValue.slice(selectionStart, selectionEnd);
                    textareaEl.setRangeText(strBetween.replace('\n', '\n  '));
                } else {  // 写入一个tab
                    textareaEl.setRangeText("  ");
                    textareaEl.selectionStart += 2;
                }
            }
        }

    })

</script>

</body>
</html>